package datastructure.list;

import java.util.function.BiConsumer;
import java.util.function.Consumer;

public class CList<T> {
    CListNode<T> head = new CListNode<>();

    public int size() {
        Integer[] sizeRef = new Integer[]{0};
        this.visit((index, node) -> {
            sizeRef[0] = index;
        });
        return sizeRef[0];
    }

    public boolean isEmpty() {
        return head.next == null;
    }

    public boolean contains(T o) {
        boolean[] resultRef = new boolean[]{false};
        this.visit((index, node) -> {
            if (node.data == o) {
                resultRef[0] = true;
            }
        });
        return resultRef[0];
    }

    public boolean add(T t) {
        return false;
    }

    public void remove(T o) {
        CListNode<T> node = head;
        while (node.next != null) {
            if (node.next.data == o) {
                node.next = node.next.next;
                return;
            } else {
                node = node.next;
            }
        }
    }

    public void remove() {
        CListNode<T> node = head;
        while (node.next != null) {
            if (node.next.next == null) {
                node.next = null;
            } else {
                node = node.next;
            }
        }
    }

    public void clear() {
        head.next = null;
    }

    public T get(int index) {
        CListNode<T>[] refs = new CListNode[1];
        this.visit(index, it -> {
            refs[0] = it;
        });
        return refs[0] == null ? refs[0].data : null;
    }

    private void visit(BiConsumer<Integer, CListNode<T>> onVisit) {
        CListNode<T> node = head;
        for (int i = 0; node.next != null; i++) {
            onVisit.accept(i, node.next);
            node = node.next;
        }
    }

    private void visit(int index, Consumer<CListNode<T>> onVisit) {
        CListNode<T> node = head;
        for (int i = 0; node.next != null; i++) {
            if (index == i) {
                onVisit.accept(node.next);
                break;
            }
            node = node.next;
        }
    }

    public void set(int index, T element) {
        this.visit(index, it -> {
            it.data = element;
        });
    }

    public void add(int index, T element) {
        BiConsumer<CListNode<T>, T> handler = (before, data) -> {
            CListNode<T> tail = before.next;
            CListNode<T> toInsert = new CListNode<>();
            toInsert.data = element;
            before.next = toInsert;
            toInsert.next = tail;
        };
        if (index == 0) {
            handler.accept(head, element);
        } else {
            this.visit(index - 1, it -> {
                handler.accept(it, element);
            });
        }
    }

    public int indexOf(T o) {
        Integer[] indexRef = new Integer[]{-1};
        this.visit((i, data) -> {
            if (o == data) {
                indexRef[0] = i;
            }
        });
        return indexRef[0];
    }
}
